package com.bootdo.rabbitMQ.config;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author wukq
 * @Date: 2020/4/11
 * @Description: topic交换机，路由模式，交换机根据路由发送消息，队列根据绑定的路由接收消息
 */
@Configuration
public class RabbitMqConfig {

    /**
     * 声明交换机，代码里面可以新建虚拟机
     * 直连型交换机，根据消息携带的路由键将消息投递给对应队列。
     */
    @Bean
    DirectExchange directExchange() {
        return new DirectExchange(RabbitConstant.EXCHANGE);
    }


    /**
     * 声明队列
     */
    @Bean
    public Queue queueTransaction() {
        //true表示持久化改队列
        return new Queue(RabbitConstant.QUEUE_TRANSACTION, true);
    }


    @Bean
    public Queue queueLogin() {
        // true表示持久化该队列
        return new Queue(RabbitConstant.QUEUE_LOGIN, true);
    }

    @Bean
    public Queue queueWarningMsg() {
        // true表示持久化该队列
        return new Queue(RabbitConstant.QUEUE_WARNING_MSG, true);
    }

    @Bean
    public Queue queueLastLogin() {
        // true表示持久化该队列
        return new Queue(RabbitConstant.QUEUE_LAST_LOGIN, true);
    }

    @Bean
    public Queue queueUploadData() {
        return new Queue(RabbitConstant.QUEUE_UPLOAD_DATA, true);
    }


    /**
     * 数据上传，死信队列
     * 在队列上指定一个Exchange，则在该队列上发生如下情况，
     * 1.消息被拒绝（basic.reject or basic.nack)，且requeue=false
     * 2.消息过期而被删除（TTL）
     * 3.消息数量超过队列最大限制而被删除
     * 4.消息总大小超过队列最大限制而被删除
     * <p>
     * 当一个消息在一个队列中变成死信之后，它能被重新推送到另一个 Exchange，这个 Exchange 就是 DLX。
     * 死信队列也是一个正常的队列，只是其他队列里的消息变成死信的时候，往这个队列转发。
     * 转发的配置
     * <p>
     * arguments
     * 1. x-message-ttl(Time-To-Live)：
     * 设置队列中的所有消息的生存周期(统一为整个队列的所有消息设置生命周期), 也可以在发布消息的时候单独为某个消息指定剩余生存时间,单位毫秒。生存时间到了，消息会被从队里中删除，注意是消息被删除，而不是队列被删除。
     * <p>
     * <p>
     * 4.x-max-length-bytes ：限定队列最大占用的内存空间大小。
     * 5.x-dead-letter-exchange ： 将从队列中删除的消息(大于最大长度、或者过期的等)推送到指定的交换机中去而不是丢弃掉。
     * 6.x-dead-letter-routing-key ：将删除的消息推送到指定交换机的指定路由键的队列中去。
     */
//    @Bean
//    public Queue queueUploadDataDead() {
//        Map<String, Object> args = new HashMap<>();
//        //声明死信交换器
//        args.put("x-dead-letter-exchange", RabbitConstant.EXCHANGE);
//        //声明死信路由键
//        args.put("x-dead-letter-routing-key", RabbitConstant.RK_UPLOAD_DATA);
//        //声明队列消息过期时间 5000ms
//        args.put("x-message-ttl", 5000);
//
//        Queue queue = new Queue(RabbitConstant.QUEUE_UPLOAD_DATA_DEAD, true, false, false, args);
//        return queue;
//    }

    /**
     * 绑定路由
     */
    @Bean
    public Binding bindingSmsPush() {
        return BindingBuilder.bind(queueSmsPush()).to(directExchange()).with(RabbitConstant.RK_SMS_PUSH);
    }

    /**
     * 声明队列
     */
    @Bean
    public Queue queueSmsPush() {
        return new Queue(RabbitConstant.QUEUE_SMS_PUSH, true);
    }


    /**
     * 消息确认回调函数
     * 先从总体的情况分析，推送消息存在四种情况：
     * <p>
     * 1:消息推送到server，但是在server里找不到交换机    1这种情况触发的是 ConfirmCallback 回调函数
     * 2:消息推送到server，找到交换机了，但是没找到队列  2这种情况触发的是 ConfirmCallback和RetrunCallback两个回调函数。
     * 3:消息推送到sever，交换机和队列啥都没找到         3这种情况触发的是 ConfirmCallback 回调函数。
     * 4:消息推送成功                                    4这种情况触发的是 ConfirmCallback 回调函数
     * 注释@Bean,代码里面@Sender里面的init()方法配置了confirmCallback()和ReturenCallback（）回调函数的，dev里面的配置也用不着了
     * Sender这个类实现了RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback 这2个接口的回调方法
     */
    //@Bean
    public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        rabbitTemplate.setConnectionFactory(connectionFactory);
        //设置开启Mandatory,才能出发回调函数，无论消息推送结果怎么样都强制调用回调函数
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("ConfirmCallback:     " + "相关数据：" + correlationData);
                System.out.println("ConfirmCallback:     " + "确认情况：" + ack);
                System.out.println("ConfirmCallback:     " + "原因：" + cause);

            }
        });

        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("ReturnCallback:     " + "消息：" + message);
                System.out.println("ReturnCallback:     " + "回应码：" + replyCode);
                System.out.println("ReturnCallback:     " + "回应信息：" + replyText);
                System.out.println("ReturnCallback:     " + "交换机：" + exchange);
                System.out.println("ReturnCallback:     " + "路由键：" + routingKey);

            }
        });
        return rabbitTemplate;
    }

}
